//*************************************************************************************************
// OLEHELP.H
//
// This header file defines a set of helper functions that makes writing OLE classes a little bit
// easier.
//*************************************************************************************************
#ifndef __OLEHELP_H__
#define __OLEHELP_H__

// Macros for interface reference counts.
#ifdef _DEBUG
#define DefineIRef() DWORD m_cRef
#define InitIRef() m_cRef(0),
#define IncIRef() ++m_cRef
#define DecIRef() --m_cRef
#else
#define DefineIRef()
#define InitIRef()
#define IncIRef()
#define DecIRef()
#endif

// Information about a cached typelib to optimize OLE automation.
struct CACHEDTYPELIB
{
	LCID		lcid;						// TypeLib's locale.
	ITypeLib	*pITypeLib;					// The TypeLib.
	ITypeInfo	*pITypeInfo;				// Last TypeInfo accessed.
	IID			sIID;						//@todo: Move this.
};

//*************************************************************************************************
//*************************************************************************************************
class CTypeInfo
{
	static GUID m_TLBID;					// Type library ID.
	static LPCWSTR m_szTLB;					// Name of the type library.
	static int m_iTypeLibMajorVersion;		// Major version of type library.
	static int m_iTypeLibMinorVersion;		// Minor version of type library.
	static LCID m_lcid;						// LCID of the TLB we loaded.
	static ITypeLib *m_pITypeLib;			// Typelib we are using.
	IID			m_sIID;						// The interface ID.
	ITypeInfo	*m_pITypeInfo;				// Typeinfo we are storing.

public:
	CTypeInfo(
		REFIID		sIID) :					// Interface type info is for.
		m_sIID(sIID),
		m_pITypeInfo(NULL)
	{
	}
	~CTypeInfo()
	{
		if (m_pITypeInfo != NULL)
		{
			m_pITypeInfo->Release();
		}
	}
	static void Terminate()
	{
		if (m_pITypeLib != NULL)
		{
			m_pITypeLib->Release();
		}
	}

	//*********************************************************************************************
	//*********************************************************************************************
	HRESULT GetTypeInfo(					// Did we get the typeinfo?
		UINT		iInfo,					// TypeInfo index.
		LCID		lcid,					// Locale.
		ITypeInfo	**ppITypeInfo);			// Type info.

	//*********************************************************************************************
	//*********************************************************************************************
	HRESULT GetIDsOfNames(					// S_OK or error.
		REFIID		sIID,					// Interface ID (future use).
		OLECHAR		**pszNames,				// Names to get IDs of.
		UINT		iNames,					// # of names in list.
		LCID		lcid,					// Locale of the names.
		DISPID		*piDispID);				// Buffer for list of DISPIDs.

	//*****************************************************************************
	//*****************************************************************************
	HRESULT Invoke(							// S_OK or error.
		IDispatch	*pIIface,				// The interface to invoke.
		DISPID		iDispID,				// Dispatch ID we are invoking.
		REFIID		sIID,					// Interface ID.
		LCID		lcid,					// Locale of the names.
		USHORT		iFlags,					// Dispatch flags.
		DISPPARAMS	*psParams,				// Dispatch parameters.
		VARIANT		*psResult,				// Returned result.
		EXCEPINFO	*psExcep,				// Exception info.
		UINT		*piArgErr);				// Index of invalid argument.
};

//*************************************************************************************************
// Register the basic information in the system registry for a given object class.
//*************************************************************************************************
int RegisterClass(							// -1 or 0.
	REFCLSID	rclsid,						// Class ID we are registering.
	LPCTSTR		szDesc,						// Class description.
	LPCTSTR		szProgID,					// Class prog ID.
	LPCTSTR		szIndepProgID,				// Class version independant prog ID.
	LPCTSTR		szModule);					// Spec for this DLL.

//*************************************************************************************************
// Unregister the basic information in the system registry for a given object class.
//*************************************************************************************************
int UnregisterClass(						// -1 or 0.
	REFCLSID	rclsid,						// Class ID we are registering.
	LPCTSTR		szProgID,					// Class prog ID.
	LPCTSTR		szIndepProgID);				// Class version independant prog ID.

//*************************************************************************************************
// Remove the registry keys for a type library.
//*************************************************************************************************
int UnregisterTypeLib(						// -1 or 0
	REFGUID		rtlbid,						// TypeLib ID we are registering.
	LPCTSTR		szVersion);					// TypeLib version.

//*************************************************************************************************
// This is a utility function to allocate a SafeArray.
//*************************************************************************************************
inline SAFEARRAY *AllocSafeArrayLen(		// Return status.
	BYTE		*pData,						// Data to be placed in the array.
	ULONG		iSize)						// Size of data.
{
	SAFEARRAYBOUND sBound;					// Used to fill out the bounds.
	SAFEARRAY	*pArray;					// Ptr to the new array.

	sBound.cElements = iSize;
	sBound.lLbound = 0;
	if ((pArray = SafeArrayCreate(VT_UI1, 1, &sBound)) != NULL)
	{
		memcpy(pArray->pvData, (void *) pData, iSize);
	}
	return pArray;
}

//*************************************************************************************************
// Get the # of bytes in the safe array.
//*************************************************************************************************
inline int SafeArrayGetDatasize(			// Size of the SafeArray data.
	SAFEARRAY	*psArray)					// Pointer to the SafeArray.
{
	int			iElems = 1;					// # of elements in the array.
	int			i;							// Loop control.

	// Compute the # of elements in the array.
	for (i = 0; i < psArray->cDims; ++i)
	{
		iElems *= psArray->rgsabound[i].cElements;
	}

	// Return the size.
	return iElems * psArray->cbElements;
}

//TODO: Do I need all of these LPTSTR helpers?
/*
//*************************************************************************************************
// Convert a variant into an SBCS string.  Return an error if the type can not be coerced.
//*************************************************************************************************
HRESULT VariantToLPTSTR(					// Return status.
	VARIANT		&sVar,						// Variant to convert.
	__out_z __out_ecount(iSize) LPTSTR szStr, // Buffer to convert into.
	int			iSize);						// Size of the buffer.

//*************************************************************************************************
// Convert a Variant into a new LPTSTR.  This involves allocating the memory and converting from
// UNICODE to ansi.
//*************************************************************************************************
HRESULT VariantToNewLPTSTR(					// Return status.
	VARIANT		sStr,						// VARIANT to convert.
	__out_z __deref_out LPTSTR *pszStr);	// Buffer for ptr to new ansi str.
*/
/*
//*************************************************************************************************
// Convert a BSTR into a LPTSTR.  This involves converting from UNICODE to ansi.
//*************************************************************************************************
inline HRESULT BSTRToLPTSTR(			// Return status.
	BSTR		sStr,					// BSTR to convert.
	__out_z __out_ecount(iMax) LPTSTR szStr, // Buffer for ansi str.
	int			iMax)					// Size of supplied buffer.
{
	int iUsedLen = 0;

	if (iMax <= 0)
	{
		assert (FALSE);
		return E_FAIL;
	}

	// if we get passed a null bstr, convert it to a null string
	if (NULL == sStr)
	{
		szStr[0] = '\0';
		return S_OK;
	}

	// When we are interested in characters that cannot be converted correctly, we should also check
	// for characters that cannot be mapped 1:1
	// e.g.: Japanese chars with Enu codepage gets translated to default '?', good
	// e.g.: Romanian (sh), (tz) gets translated to best fit 's', 't' with default En code page when
	// WC_NO_BEST_FIT_CHARS is not specified, bad
	DWORD dwFlags = WC_NO_BEST_FIT_CHARS;
	BOOL fUsedDefaultChar = FALSE;

	// Convert to MBCS.
	iUsedLen = WideCharToMultiByte(CP_ACP, dwFlags, sStr , -1, szStr, iMax * sizeof(TCHAR),
								   NULL, &fUsedDefaultChar);

    if (0 == iUsedLen)
	{
		DWORD dwErr = GetLastError();
		if (ERROR_INSUFFICIENT_BUFFER == dwErr)
		{
			return DISP_E_OVERFLOW;
		}
		else
		{
			return E_FAIL;
		}
    }

	// If default character was used, the Unicode string cannot be correctly represented 
	// in the current code page, so we'll fail the SSAPI call
	if (fUsedDefaultChar)
	{
		return HRESULT_FROM_WIN32(ERROR_NO_UNICODE_TRANSLATION);
	}

	return S_OK;
}
*/
/*
//*************************************************************************************************
// Convert a BSTR into a new LPTSTR.  This involves allocating the memory and converting from
// UNICODE to ansi.
//*************************************************************************************************
HRESULT BSTRToNewLPTSTR(				// Return status.
	BSTR		sStr,					// BSTR to convert.
	__out_z __deref_out LPTSTR *pszStr);// Buffer for ptr to new ansi str.

//*************************************************************************************************
// Converts a LPCTSTR into a new BSTR, returning the newly allocated BSTR in the given pointer
//*************************************************************************************************
HRESULT LPCTSTRToNewBSTR(				// Return status.
	BSTR		*psName,				// Return BSTR here.
	LPCTSTR		szStr);					// String to convert.
*/
//*************************************************************************************************
// Convert a variant to either an integer or a string.  This is used by collections to coerce the
// variant.
//*************************************************************************************************
HRESULT VariantToIntOrStr(
	VARIANT const *pVar,				// Variant to coerce
	LONG		*pl,					// resulting long, if *pfString==FALSE
	BSTR		*pbstr,					// resulting string, if *pfString==TRUE
	BOOL		*pfString);				// Was is string or integer?

//*************************************************************************************************
// Return a boolean indicating if the specified variant represents an optional parameter that has
// been omitted from the supplied parameter list.
//*************************************************************************************************
inline BOOL ParamOmitted(				// TRUE or FALSE.
	VARIANT		&sVar)					// Variant to check.
{
	return (sVar.vt == VT_ERROR && sVar.scode == DISP_E_PARAMNOTFOUND);
}

#endif // __OLEHELP_H__